/*
 * HuffmanArbol.cpp
 *
 *  Created on: 10/06/2014
 *      Author: manuel
 */

#include "HuffmanArbol.h"

HuffmanArbol::HuffmanArbol() {
	raiz=NULL;
	actual = NULL;
}

HuffmanArbol::HuffmanArbol(HuffNodo *rz) {
	raiz = rz;
	actual = rz;
}

HuffmanArbol::HuffmanArbol(Tabla tb) : tabla(tb){
	raiz = NULL;
	actual = NULL;
}

HuffmanArbol::~HuffmanArbol() {

	if( raiz != NULL)
		delete raiz;

	if( (actual != NULL) && (actual != raiz) )
		delete actual;
}

void HuffmanArbol::setTabla(Tabla tb){
	tabla = tb;
}

void imprimirArbol(HuffNodo* nodo) {
	if( nodo->esHoja() ){
		return;
	}

	imprimirArbol(nodo->hijoIzq);
	imprimirArbol(nodo->hijoDer);

	std::cout << *nodo->hijoIzq;
	std::cout << *nodo->hijoDer;
}

void destruirArbolExceptoHojas(HuffNodo* nodo) {
	if( nodo->esHoja() ){
		return;
	}
	destruirArbolExceptoHojas(nodo->hijoIzq);
	destruirArbolExceptoHojas(nodo->hijoDer);

	if( !nodo->hijoIzq->esHoja() )
		delete nodo->hijoIzq;

	if( !nodo->hijoDer->esHoja() )
		delete nodo->hijoDer;
}

void HuffmanArbol::generarArbol() {
	Tabla::iterator it, it2;

	HuffNodo *aux;
	HuffNodo *hIzq;
	HuffNodo *hDer;

	double fq1, fq2;
	while( tabla.size() > 1 ) {
//		printf("tamaño: %d\n", (int)tabla.size());

		it = tabla.begin();
		it2 = tabla.begin(); it2++;

		hIzq = (*it).second;
		fq1 = hIzq->getFreq();

		hDer = (*it2).second;
		fq2 = hDer->getFreq();

		aux = new HuffNodo(fq1+fq2, 0, hIzq, hDer);

//		std::cout << "it: " << *(*it).second;
//		std::cout << "it2: " << *(*it2).second;

		tabla.insert(Tabla::value_type(aux->getFreq(), aux));
		tabla.erase(it);
		tabla.erase(it2);
	}
	it = tabla.begin();
	raiz = (*it).second;
}

void HuffmanArbol::generarArbolOptimizado() {
	Tabla::iterator it, it2;

	HuffNodo *aux;
	HuffNodo *hIzq;
	HuffNodo *hDer;

	double fq1, fq2;
	while( tabla.size() > 1 ) {
//		printf("tamaño: %d\n", (int)tabla.size());

		it = tabla.begin();
		it2 = tabla.begin(); it2++;

		hIzq = (*it).second;
		fq1 = hIzq->getFreq();

		hDer = (*it2).second;
		fq2 = hDer->getFreq();

		aux = new HuffNodo(fq1+fq2, 0, hIzq, hDer);

		tabla.insert(Tabla::value_type(aux->getFreq(), aux));
		tabla.erase(it);
		tabla.erase(it2);
	}
	it = tabla.begin();
	HuffNodo *nodoCero = new HuffNodo(0, 0, NULL, NULL);

	raiz = new HuffNodo( (*it).second->getFreq(), 0, nodoCero, (*it).second );
}

void buscarCharEnArbol(HuffNodo* nodo, std::string *string, unsigned short nivel,
			std::string bit, unsigned char caracter, std::string *cod, bool *encontrado)
{
	string->resize(nivel);
	(*string) += bit;

	if( nodo->esHoja() )
	{
		if( nodo->getCaracter() == caracter )
		{
			string->erase(string->begin()+0);
			*cod = *string;
//			std::cout << "charRrR: " << nodo->getCaracter() << " codif: " << *cod <<
//					" freq: " << nodo->getFreq() << "  size: "<< cod->size() << '\n';
			*encontrado = true;
		}
		return;
	}

	if( !(*encontrado) )
		buscarCharEnArbol(nodo->hijoIzq, string, nivel+1, "0", caracter, cod, encontrado);
	if( !(*encontrado) )
		buscarCharEnArbol(nodo->hijoDer, string, nivel+1, "1", caracter, cod, encontrado);
}

bool HuffmanArbol::getCodificacionChar2(unsigned char caracter, std::string& cod) {
	std::string temporal;
	std::string codif;
	bool encontrado = false;

	// OPTIMIZACION DE LA EMISION DEL 0 CON 1 BIT
	if( caracter == 0 ){
		cod = "0";
		return true;
	}

	buscarCharEnArbol(raiz, &temporal, 0,	"", caracter, &codif, &encontrado);

	if( encontrado ){
		cod = codif;
		return true;
	}else
		return false;
}

bool HuffmanArbol::moverActualHastaHoja(std::string direccion){

	//std::cout << "direccion: " << direccion <<'\n';

	if( direccion == "0" )
		actual = actual->hijoIzq;
	else
		actual = actual->hijoDer;

	if( actual->esHoja() )
		return true;

	return false;
}

void HuffmanArbol::moverActualARaiz() {
	actual = raiz;
}

unsigned char HuffmanArbol::getCaracterNodoActual() {
	return actual->getCaracter();
}

void HuffmanArbol::destruir() {
	destruirArbolExceptoHojas(raiz);
	raiz->hijoDer = NULL; raiz->hijoIzq = NULL;
}

void HuffmanArbol::destruirOptimizado() {
	destruirArbolExceptoHojas(raiz);
	//elimino el nodo del 0;
	delete raiz->hijoIzq;

	raiz->hijoDer = NULL; raiz->hijoIzq = NULL;
}

void HuffmanArbol::imprimir() {
	if( raiz!= NULL ){
		printf("Arbol:\n");
		imprimirArbol(raiz);
	}
	else
		std::cout << "ARBOL VACIO\n";
}

